package com.tianji.remark.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constants.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author xzh
 * @since 2024-03-16
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_={@Lazy} )
public class LikedRecordRedisServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    private final RabbitMqHelper mqHelper;

    private final StringRedisTemplate redisTemplate;

    /**
     * 增加或取消点赞
     */

    @Override
    @Transactional
    public void addLikeRecord(LikeRecordFormDTO dto) {
        //1.先根据liked判断是点赞还是取消点赞
        Long userId = UserContext.getUser();
        Boolean result=dto.getLiked() ? like(dto):unlike(dto);
        if (!result){
            return;
        }
        //2.统计点赞数量
        String key=RedisConstants.LIKE_BIZ_KEY_PREFIX+dto.getBizId();
        Long likedTimes = redisTemplate.opsForSet().size(key);
        if (likedTimes==null){
            return;
        }

        //3.更新点赞数量redis
        //add  有记录则更新 没有则新增
        //添加到 value 排序集 处 key，或更新其 score （如果已存在）。
        redisTemplate.opsForZSet().add(RedisConstants.LIKE_COUNT_KEY_PREFIX+dto.getBizType(),
                dto.getBizId().toString(),
                likedTimes);
    }

    private Boolean unlike(LikeRecordFormDTO dto) {
        Long userId = UserContext.getUser();
        //取消点赞
        String key= RedisConstants.LIKE_BIZ_KEY_PREFIX+dto.getBizId();
        Long result = redisTemplate.opsForSet().remove(key, userId.toString());
        //删除成功返回1 失败返回0
        return result!=null&&result>0;
    }

    private Boolean like(LikeRecordFormDTO dto) {
        Long userId = UserContext.getUser();
        //1.新增点赞
        String key= RedisConstants.LIKE_BIZ_KEY_PREFIX+dto.getBizId();
        Long reult = redisTemplate.opsForSet().add(key, userId.toString());
        //新增成功返回1 失败返回0
        return reult!=null&&reult>0;
    }


    /**
     * 查询用户是否对某个业务点赞
     * @param bizIds
     * @return
     */
    @Override
    public Set<Long> isBizLiked(List<Long> bizIds) {

        //1.查询出该用户在bizIds给那些点过赞返回Boolean集合
        Long userId = UserContext.getUser();
        //用管道  Pipeline
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            //connection 强转为String
            StringRedisConnection src = (StringRedisConnection) connection;
            for (int i = 0; i < bizIds.size(); i++) {
                String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizIds.get(i);
                src.sIsMember(key, userId.toString());
            }
            return null;
        });
        /*List<Object> objects = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //connection 强转为String
                StringRedisConnection src = (StringRedisConnection) connection;
                for (int i = 0; i < bizIds.size(); i++) {
                    String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizIds.get(i);
                    src.sIsMember(key, userId.toString());
                }
                return null;
            }
        });*/

        //Pipeline管道 是有序的i的顺序不变
//        Set<Long> set=new HashSet<>();
//        for (int i = 0; i < objects.size(); i++) {
//            if ((Boolean) objects.get(i)){
//                set.add(bizIds.get(i));
//            }
//        }

        Set<Long> set2 = IntStream.range(0, objects.size())//创建从0到size长度的流
                .filter(i -> (Boolean) objects.get(i))//filter里面的值为ture才会留下 正好得到自己想要的结果
                .mapToObj(bizIds::get)//用角标i取bizIds中对应的数据,就是点过赞的id
                .collect(Collectors.toSet());//收集

        return set2;
    }

    @Override
    public void readLikedTimesAndSentMessages(String bizType, int maxBizSize) {
        String key=RedisConstants.LIKE_COUNT_KEY_PREFIX+bizType;

        //popMin 从得分最少的开始取值  取到值并删除  遵循原子性
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate
                .opsForZSet().popMin(key, maxBizSize);
        if (CollUtils.isEmpty(tuples)){
            return;
        }
        //数据转换
        List<LikedTimesDTO> list=new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            String bizId = tuple.getValue();
            Double likedTimes = tuple.getScore();
            if (bizId==null||likedTimes==null){
                return;
            }
            LikedTimesDTO dto=new LikedTimesDTO(Long.valueOf(bizId),likedTimes.intValue());
            list.add(dto);
        }
        //发送mq通知
        mqHelper.send(
                LIKE_RECORD_EXCHANGE,
                StringUtils.format(LIKED_TIMES_KEY_TEMPLATE,bizType),
                list
        );


    }
}
